#include <stdint.h>
#include <windows.h>
#include <stdio.h>
#include <fstream>
#include <iomanip>
#include <ctime>
#include <stdint.h> // Ten nagłówek definiuje uintptr_t
#include <psapi.h> // <--- To jest wymagane dla GetModuleInformation

#define nullptr NULL

// --- Adresy funkcji silnika GTA SA (v1.0 US) ---

//---------------------------------------------------------
typedef void(__cdecl* tPrintHelpMessage)(const char* text, bool bQuick, bool bPermanent, bool bCheckGxt);
tPrintHelpMessage PrintHelpMessage = (tPrintHelpMessage)0x588BE0;
//Adres US 1.0: 0x588BE0
//Adres EU 1.0: 0x588C40
//Wzorzec (Pattern): 55 8B EC 51 53 56 8B 75 08 57
//Maska: xxxxxxxxxx
//---------------------------------------------------------
typedef void(__cdecl* tRequestModel)(int modelID, int flag);
tRequestModel RequestModel = (tRequestModel)0x4087E0;
//Adres US 1.0: 0x4087E0
//Adres EU 1.0: 0x408840
//Wzorzec: \x8B\x44\x24\x04\x8B\x54\x24\x08\x8B\x0C\xC5
//Maska: xxxxxxxxxxx
//---------------------------------------------------------
typedef void(__cdecl* tLoadAllRequestedModels)(bool bPriority);
tLoadAllRequestedModels LoadAllRequestedModels = (tLoadAllRequestedModels)0x40EA10;
//Adres US 1.0: 0x40EA10
//Adres EU 1.0: 0x40EA70
//Wzorzec: \x80\x3D\x00\x00\x00\x00\x00\x74\x06\xC6\x05
//Maska: xx?????xxxx
//---------------------------------------------------------
typedef bool(__cdecl* tHasModelLoaded)(int modelID);
tHasModelLoaded HasModelLoaded = (tHasModelLoaded)0x407800;
//Adres US 1.0: 0x407800
//Adres EU 1.0: 0x407860
//Wzorzec: \x8B\x44\x24\x04\x85\xC0\x78\x10\x8B\x0D
//Maska: xxxxxxxxxx
//---------------------------------------------------------
//typedef void(__cdecl* tSetModelIsNoLongerNeeded)(int modelID);
//Adres US 1.0: 0x408270
//Adres EU 1.0: 0x4082D0
//Wzorzec \x8B\x44\x24\x04\x85\xC0\x78\x0E\x8B\x0D
//Maska: xxxxxxxxxx
//---------------------------------------------------------
typedef void(__fastcall* tGiveWeapon)(uintptr_t pPed, int weaponID, int ammo, bool bLikeUnused);
tGiveWeapon GiveWeapon = (tGiveWeapon)0x5E6080;//0x5E4790; // Poprawny adres dla v1.0 US
//Adres US 1.0: 0x005E6080 
//Adres EU 1.0: 0x005E60E0
//Wzorzec  0F B6 44 24 04 56 8B F1
//Maska: xxxxxxxx
//---------------------------------------------------------
typedef void(__cdecl* SpawnVehicleFunc)(int modelID);
SpawnVehicleFunc CreateVehicle = (SpawnVehicleFunc)0x43A0B6; //EU 1.0 0x43A126
//US 1.0: 0x0043A0B6
//EU 1.0: 0x0043A126
//Wzorzec (Pattern): "\x56\x8B\xF1\x8B\x46\x14\x85\xC0" 
//Maska: "xxxxxxxx"
//---------------------------------------------------------
typedef void(__fastcall* tSetCurrentWeapon)(uintptr_t pPed, int weaponID);
tSetCurrentWeapon SetCurrentWeapon = (tSetCurrentWeapon)0x5E3220; // Adres v1.0 
//Adres US 1.0: 0x5E3220
//Adres EU 1.0: 0x5E3280
//Wzorzec (Pattern): \x8B\x44\x24\x04\x56\x8B\xF1\x8B\x06\xFF\x90
//Maska: xxxxxxxxxxx
//-----------------------------------------------------------
typedef void(__cdecl* tPrintString)(const char* text, unsigned int time, unsigned short flag, bool bCheckGxt);
tPrintString PrintString = (tPrintString)0x69F1E0;
//Adres US 1.0: 0x69F2B0 (CMessages::AddMessage)
//Wzorzec: \x55\x8B\xEC\x83\xEC\x10\x53\x56\x57\x8D\x45\x10
//Maska: xxxxxxxxxxxx
//-----------------------------------------------------------
typedef int(__cdecl* tGetModelIdFromWeapon)(int weaponID);
tGetModelIdFromWeapon GetModelIdFromWeapon = (tGetModelIdFromWeapon)0x438270;
//Adres US 1.0: 0x438270
//Adres EU 1.0: 0x4382D0
//Wzorzec (Pattern): \x0F\xB6\x44\x24\x04\x69\xC0\x70\x00\x00\x00\x05
//Maska: xxxxxxxxxxxx
//-----------------------------------------------------------
//typedef void(__cdecl* SpawnVehicleFunc)(int modelID);
//SpawnVehicleFunc CreateVehicle = (SpawnVehicleFunc)0x43A0B6; //EU 1.0 0x43A126
//Cheat Spawn	
//Adres US 1.0 0x43A0B6	
//Adres EU 1.0 0x43A126	
//56 8B F1 8B 46 14 85 C0
//Maska: xxxxxxxx
//-----------------------------------------------------------
//typedef void*(__cdecl* tCreateVehicle)(int modelID, float x, float y, float z);
//tCreateVehicle CreateVehicle = (tCreateVehicle)0x431F80;
//Engine Create	
//0x431F80	
//0x431FE0	
//FF 74 24 10 FF 74 24 0C FF 74 24 08
//Maska: xxxxxxxxxxxx
/*
unsigned char* addr = FindPattern(
    "\xFF\x74\x24\x10\xFF\x74\x24\x0C\xFF\x74\x24\x08", 
    "xxxxxxxxxxxx"
);

if (addr) {
    typedef void*(__cdecl* tCreateVehicle)(int modelID, float x, float y, float z);
    tCreateVehicle EngineCreate = (tCreateVehicle)addr;
    
    // Pamiętaj o RequestModel przed wywołaniem!
    void* myCar = EngineCreate(411, 2485.0f, -1667.0f, 13.0f); 
}
*/
typedef void(__fastcall* tSetModelIndex)(void* ped, unsigned int modelID);
tSetModelIndex SetModelIndex = (tSetModelIndex)0x5E4880;
//Adres US 1.0: 0x5E4880
//Adres EU 1.0: 0x5E48E0
//Wzorzec (Pattern): \x56\x8B\xF1\x8B\x44\x24\x08\x57\x8B\x7E\x14
//Maska: xxxxxxxxxxx


typedef void(__cdecl* tCheatSpawnVehicle)(int vehicleID);
tCheatSpawnVehicle CheatSpawnVehicle = (tCheatSpawnVehicle)0x43A0B0;
//EU 1.0: 0x43A110 (odpowiednik 0x43A0B0)
//EU 1.0: 0x43A126 (odpowiednik 0x43A0B6)





const char* vehicleNames[] = {
    "LANDSTAL", "BRAVURA", "BUFFALO", "LINERUN", "PEREN", "SENTINEL", "DUMPER", "FIRETRUK", "TRASH", "STRETCH", "MANANA",
    "INFERNUS", "VOODOO", "PONY", "MULE", "CHEETAH", "AMBULAN", "LEVIATHN", "MOONBEAM", "ESPERANT", "TAXI", "WASHING", "BOBCAT",
    "MRWHOOP", "BFINJECT", "HUNTER", "PREMIER", "ENFORCER", "SECURICA", "BANSHEE", "PREDATOR", "BUS", "RHINO", "BARRACKS",
    "HOTKNIFE", "ARTICT1", "PREVION", "COACH", "CABBIE", "STALLION", "RUMPO", "RCBANDIT", "ROMERO", "PACKER", "MONSTER",
    "ADMIRAL", "SQUALO", "SEASPAR", "PIZZABOY", "TRAM", "ARTICT2", "TURISMO", "SPEEDER", "REEFER", "TROPIC", "FLATBED", "YANKEE",
    "CADDY", "SOLAIR", "TOPFUN", "SKIMMER", "PCJ600", "FAGGIO", "FREEWAY", "RCBARON", "RCRAIDER", "GLENDALE", "OCEANIC",
    "SANCHEZ", "SPARROW", "PATRIOT", "QUAD", "COASTG", "DINGHY", "HERMES", "SABRE", "RUSTLER", "ZR350", "WALTON", "REGINA",
    "COMET", "BMX", "BURRITO", "CAMPER", "MARQUIS", "BAGGAGE", "DOZER", "MAVERICK", "VCNMAV", "RANCHER", "FBIRANCH", "VIRGO",
    "GREENWOO", "JETMAX", "HOTRING", "SANDKING", "BLISTAC", "POLMAV", "BOXVILLE", "BENSON", "MESA", "RCGOBLIN", "HOTRINA",
    "HOTRINB", "BLOODRA", "RNCHLURE", "SUPERGT", "ELEGANT", "JOURNEY", "BIKE", "MTBIKE", "BEAGLE", "CROPDUST", "STUNT", "PETRO",
    "RDTRAIN", "NEBULA", "MAJESTIC", "BUCCANEE", "SHAMAL", "HYDRA", "FCR900", "NRG500", "COPBIKE", "CEMENT", "TOWTRUCK",
    "FORTUNE", "CADRONA", "FBITRUCK", "WILLARD", "FORKLIFT", "TRACTOR", "COMBINE", "FELTZER", "REMINGTN", "SLAMVAN", "BLADE",
    "FREIGHT", "STREAK", "VORTEX", "VINCENT", "BULLET", "CLOVER", "SADLER", "FIRELA", "HUSTLER", "INTRUDER", "PRIMO", "CARGOBOB",
    "TAMPA", "SUNRISE", "MERIT", "UTILITY", "NEVADA", "YOSEMITE", "WINDSOR", "MONSTERA", "MONSTERB", "URANUS", "JESTER", "SULTAN",
    "STRATUM", "ELEGY", "RAINDANC", "RCTIGER", "FLASH", "TAHOMA", "SAVANNA", "BANDITO", "FREIFLAT", "STREAKC", "KART", "MOWER",
    "DUNERIDE", "SWEEPER", "BROADWAY", "TORNADO", "AT400", "DFT30", "HUNTLEY", "STAFFORD", "BF400", "NEWSVAN", "TUG", "PETROTR",
    "EMPEROR", "WAYFARER", "EUROS", "HOTDOG", "CLUB", "FREIBOX", "ARTICT3", "ANDROM", "DODO", "RCCAM", "LAUNCH", "COPCARLA",
    "COPCARSF", "COPCARVG", "COPCARRU", "PICADOR", "SWATVAN", "ALPHA", "PHOENIX", "GLENSHIT", "SADLSHIT", "BAGBOXA", "BAGBOXB",
    "TUGSTAIR", "BOXBURG", "FARMTR1", "UTILTR1"
};

const char* weaponNames[] = {
    "Fist",                /*0 */"Brass Knuckles",      /*1 */"Golf Club",           /*2 */
    "Nite Stick",          /*3 */"Knife",               /*4 */"Baseball Bat",        /*5 */
    "Shovel",              /*6 */"Pool Cue",            /*7 */"Katana",              /*8 */
    "Chainsaw",            /*9 */"Purple Dildo",        /*10*/"Small White Dildo",   /*11*/
    "Large White Dildo",   /*12*/"Silver Vibrator",     /*13*/"Flowers",             /*14*/
    "Cane",                /*15*/"Grenade",             /*16*/"Tear Gas",            /*17*/
    "Molotov Cocktail",    /*18*/"",                    /*19*/"",                    /*20*/
    "",                    /*21*/"9mm",                 /*22*/"Silenced 9mm",        /*23*/
    "Desert Eagle",        /*24*/"Shotgun",             /*25*/"Sawn-off Shotgun",    /*26*/
    "Combat Shotgun",      /*27*/"Micro Uzi",           /*28*/"MP5",                 /*29*/
    "AK-47",               /*30*/"M4",                  /*31*/"Tec-9",               /*32*/
    "Country Rifle",       /*33*/"Sniper Rifle",        /*34*/"Rocket Launcher",     /*35*/
    "HS Rocket Launcher",  /*36*/"Flamethrower",        /*37*/"Minigun",             /*38*/
    "Satchel Charge",      /*39*/"Detonator",           /*40*/"Spray Can",           /*41*/
    "Fire Extinguisher",   /*42*/"Camera",              /*43*/"Night Vision",        /*44*/
    "Infrared Vision",     /*45*/"Parachute"            /*46*/
};



struct GameAddresses {
    uintptr_t ASpawnVehicle;
    uintptr_t AGiveWeapon;
    uintptr_t ASetWeapon;
    uintptr_t AHelpMessage;
    uintptr_t ARequestModel;
    uintptr_t ALoadAllRequestedModels;
    uintptr_t AHasModelLoaded;
    uintptr_t ASetModelIsNoLongerNeeded;
    uintptr_t AGetModelIdFromWeapon;
} Addr;



const char* weaponSetNames[] = { "GUNS PACK 1", "GUNS PACK 2", "GUNS PACK 1" };
uintptr_t weaponSetAddrs[] = { 0x4385B0, 0x438890, 0x438B30 }; //{ 0x438E40, 0x438F20, 0x439000 };
int selectedSetIdx = 0;

// --- Zmienne stanu ---
bool bMenuOpen = false;
int menuIndex = 0;
int selectedVehicleID = 522; // NRG-500

bool bGodMode = false;
bool bInfAmmo = false;
bool bNever_wanted = false;
bool bSpeedHack = false;
bool freze_player = false;

DWORD lastScrollTime = 0;
int scrollDelay = 200; // Początkowe opóźnienie (ms)
DWORD pressStartTime = 0;
uintptr_t lastSpawnedVehicle = 0;

bool g_NeedWeapon = false;
int g_WepID = 0;
int g_Ammo = 0;

#define OFFSET_IMMUNITIES 0x440
#define OFFSET_HEALTH 0x540


// 2. Bezpieczna funkcja pobierająca nazwę z Twojej tablicy
const char* GetMyVehicleName(int id) {
    int index = id - 400; // Przesunięcie, bo auta zaczynają się od 400
    if (index >= 0 && index < 212) {
        return vehicleNames[index];
    }
    return "UNKNOWN";
}

int MyGetWeaponModel(int weaponID) {
    // Tablica indeksowana przez ID broni (0-46)
    static int weaponModelIDs[] = {
        -1, 331, 333, 334, 335, 336, 337, 338, 339, 341, // 0-9
        321, 322, 323, 324, 325, 326, 342, 343, 344, -1, // 10-19
        -1, -1, 346, 347, 348, 349, 350, 351, 352, 353, // 20-29
        355, 356, 372, 357, 358, 359, 360, 361, 362, 363, // 30-39
        364, 365, 366, 367, 368, 369, 371                // 40-46
    };

    if (weaponID >= 0 && weaponID <= 46) return weaponModelIDs[weaponID];
    return -1;
}

// --- Funkcja rysująca menu ---
void DrawMenu() {
    char buffer[1024]; // Zwiększony bufor
    char godStatus[15], ammoStatus[15], wantedStatus[15], speedStatus[15];
    const char* vName = GetMyVehicleName(selectedVehicleID);

    sprintf(godStatus, bGodMode ? "~G~ON" : "~R~OFF");
    sprintf(ammoStatus, bInfAmmo ? "~G~ON" : "~R~OFF");
    sprintf(wantedStatus, bNever_wanted ? "~G~ON" : "~R~OFF");
    sprintf(speedStatus, bSpeedHack ? "~G~ON" : "~R~OFF");

    // Budowanie całego tekstu menu
    sprintf(buffer, 
        "- ~G~ASI CHEAT MENU ~W~-~n~"
        "%s GOD MODE: %s~n~"
        "%s INF AMMO: %s~n~"
        "%s COPS OFF: %s~n~"
        "%s SPEEDHACK: %s~n~"
        "%s SPAWN: ~Y~%s~n~"
        "%s GIVE: ~Y~%s"
        "%s CHANGE SKIN: ~Y~%s",
        (menuIndex == 0 ? "~Y~>" : "~W~ "), godStatus,
        (menuIndex == 1 ? "~Y~>" : "~W~ "), ammoStatus,
        (menuIndex == 2 ? "~Y~>" : "~W~ "), wantedStatus,
        (menuIndex == 3 ? "~Y~>" : "~W~ "), speedStatus,
        (menuIndex == 4 ? "~Y~>" : "~W~ "), vName,
        (menuIndex == 5 ? "~Y~>" : "~W~ "), weaponNames[selectedSetIdx],
        (menuIndex == 6 ? "~Y~>" : "~W~ "), weaponNames[selectedSetIdx]
    );

    // Wyświetlanie (krótki czas 150ms, bo odświeżamy w pętli)
    //PrintString(buffer, 150, 1, false);
    PrintHelpMessage(buffer, true, true, false);
}

void scroller(bool left, bool right, int *value,int min, int max)
{
    if (left || right) {
        if (pressStartTime == 0) pressStartTime = GetTickCount();
        DWORD currentTime = GetTickCount();
        DWORD pressDuration = currentTime - pressStartTime;
        // Dynamiczne obliczanie opóźnienia (im dłużej trzymasz, tym mniejszy delay)
        if (pressDuration > 2000) scrollDelay = 30;      // Super szybko po 2s
        else if (pressDuration > 1000) scrollDelay = 70; // Średnio po 1s
        else if (pressDuration > 500) scrollDelay = 130; // Lekkie przyspieszenie
        else scrollDelay = 250;                          // Start (wolno)
        if (currentTime - lastScrollTime > (DWORD)scrollDelay) {
            if (right) {
                (*value)++; // POPRAWKA: nawiasy wymuszają inkrementację wartości
                if (*value > max) *value = min;
            } else {
                (*value)--; // POPRAWKA: jw.
                if (*value < min) *value = max;
            }
            lastScrollTime = currentTime;
        }
    } else {
        // Reset, gdy klawisz zostanie puszczony
        pressStartTime = 0;
    }
}

void spawn_vehicle() {
    // Sprawdzenie adresów (Twoje if-y są super)
    if(!Addr.ARequestModel || !Addr.ALoadAllRequestedModels || !Addr.AHasModelLoaded || 
       !Addr.ASetModelIsNoLongerNeeded || !Addr.ASpawnVehicle) return;

    int vID = selectedVehicleID;
    if (vID < 400 || vID > 611) return; 

    // Definicje funkcji z Twoich adresów
    
    
    typedef void(__cdecl* tRequestModel)(int modelID, int flag);
    tRequestModel RequestModel = (tRequestModel)Addr.ARequestModel;
    
    typedef void(__cdecl* tLoadAllRequestedModels)(bool bPriority);  
    tLoadAllRequestedModels LoadAllRequestedModels = (tLoadAllRequestedModels)Addr.ALoadAllRequestedModels;
    
    typedef bool(__cdecl* tHasModelLoaded)(int modelID);  
    tHasModelLoaded HasModelLoaded = (tHasModelLoaded)Addr.AHasModelLoaded;
    
    typedef void(__cdecl* tSetModelIsNoLongerNeeded)(int modelID);   
    tSetModelIsNoLongerNeeded ReleaseModel = (tSetModelIsNoLongerNeeded)Addr.ASetModelIsNoLongerNeeded;

    // 1. Zleć ładowanie
    RequestModel(vID, 6);
    LoadAllRequestedModels(false);

    // 2. KRYTYCZNE: Czekaj aż model się załaduje
    int timeout = 0;
    while (!HasModelLoaded(vID) && timeout < 1000) {
        Sleep(1); // Jeśli to osobny wątek, Sleep jest bezpieczny
        timeout++;
    }

    // 3. Jeśli model się załadował - spawnuj
    if (HasModelLoaded(vID)) {
        __asm__ __volatile__ (
            "pushl %[id]\n\t"
            "call *%[addr]\n\t"
            "addl $4, %%esp\n\t"
            : 
            : [id] "m" (vID), [addr] "r" (Addr.ASpawnVehicle)
            : "eax", "memory"
        );
        
        // 4. Powiedz grze, że już nie potrzebujesz modelu w kolejce
        //ReleaseModel(vID);
    }
}



void GivePlayerWeapon(int weaponID, int ammo) {

    
    
    
    uintptr_t addrGetModel  = 0x438270;
    uintptr_t addrRequest   = 0x4087E0;
    uintptr_t addrLoadAll   = 0x40EA10;
    uintptr_t addrHasLoaded = 0x407800;
    uintptr_t addrGive      = 0x5E6080;

    // 1. Pobierz Model ID
    int modelID = MyGetWeaponModel(weaponID);
    if (modelID == -1) return;

    // 2. Załaduj model
    ((void(__cdecl*)(int, int))addrRequest)(modelID, 6);
    ((void(__cdecl*)())addrLoadAll)();

    // 3. Czekaj (zabezpieczenie przed nieskończoną pętlą)
    for(int i = 0; i < 100; i++) {
        if (((bool(__cdecl*)(int))addrHasLoaded)(modelID)) break;
        Sleep(5);
    }

    void* pPlayer = *(void**)0xB7CD98;
    if (pPlayer && ((bool(__cdecl*)(int))addrHasLoaded)(modelID)) {
        // Uproszczony ASM - mniej zmiennych wejściowych = brak błędu GENERAL_REGS
        __asm__ __volatile__ (
            "pushl $1\n\t"              // Parametr: bLikeUnused
            "pushl %%ebx\n\t"           // Parametr: ammo (przez ebx)
            "pushl %%eax\n\t"           // Parametr: weaponID (przez eax)
            "movl %%edx, %%ecx\n\t"      // Załaduj pPlayer do ECX (thiscall)
            "call *%%edi\n\t"           // Wywołaj funkcję (przez edi)
            :
            : "a" (weaponID), "b" (ammo), "d" (pPlayer), "D" (addrGive)
            : "ecx", "memory"
        );
    }
}







void Log(const char* message, uintptr_t address = 0) {
    std::ofstream logFile("cheat_menu_log.txt", std::ios::app);
    if (logFile.is_open()) {
        // Dodajemy znacznik czasu
        std::time_t t = std::time(nullptr);
        char timeStr[20];
        std::strftime(timeStr, sizeof(timeStr), "%H:%M:%S", std::localtime(&t));

        logFile << "[" << timeStr << "] " << message;
        if (address != 0) {
            logFile << " -> 0x" << std::hex << std::uppercase << address;
        }
        logFile << std::endl;
        logFile.close();
    }
}

// Funkcja pomocnicza do szukania wzorca w pamięci
unsigned char* FindPattern(const char* pattern, const char* mask) {
    uintptr_t base = (uintptr_t)GetModuleHandle(NULL);
    uintptr_t size = 0x1000000; // 16MB - bezpieczny zasięg dla gta_sa.exe
    
    size_t patternLength = strlen(mask);
    
    for (uintptr_t i = 0; i < size - patternLength; i++) {
        bool found = true;
        for (size_t j = 0; j < patternLength; j++) {
            // KLUCZOWA POPRAWKA: rzutowanie na (unsigned char)
            if (mask[j] != '?' && (unsigned char)pattern[j] != *(unsigned char*)(base + i + j)) {
                found = false;
                break;
            }
        }
        if (found) return (unsigned char*)(base + i);
    }
    return NULL; // Używamy NULL zamiast nullptr dla Falcon C++
}


void InitializePlugin() {
    // Czyścimy stary log przy starcie
    std::ofstream("cheat_menu_log.txt", std::ios::trunc).close();
    Log("--- ASI Cheat Menu Startup ---");
    Log("--- Compatybile Addr finds ---");
    
    //Log("--- ASI Cheat Menu v1.0 US detected ---");

    // ADRESY DLA WERSJI 14 383 616 BAJTÓW (v1.0 US)
    //Addr.ASpawnVehicle               = 0x43A0B0;
    //Addr.AGiveWeapon                 = 0x5E6080;
    //Addr.ASetWeapon                  = 0x5E3220;
    //Addr.AHelpMessage                = 0x588BE0;
    //Addr.ARequestModel               = 0x4087E0;
    //Addr.ALoadAllRequestedModels      = 0x40EA10;
    //Addr.AHasModelLoaded             = 0x407800;
    //Addr.ASetModelIsNoLongerNeeded   = 0x408270;
    //Addr.AGetModelIdFromWeapon       = 0x438270;
    //Addr.AIsPlayerInAnyVehicle       = 0x4417A0;

    //Log("SpawnVehicle", Addr.SpawnVehicle);
    //Log("GiveWeapon", Addr.GiveWeapon);
    //Log("HelpMessage", Addr.HelpMessage);
    Log("--- Initialization Finished ---");

    // Skanowanie adresów
    Addr.ASpawnVehicle = (uintptr_t)FindPattern((const char*)"\x56\x8B\xF1\x8B\x46\x14\x85\xC0", "xxxxxxx");//"\x55\x8B\xEC\x53\x56\x8B\xF1"
    Log("SpawnVehicle", Addr.ASpawnVehicle);

    Addr.AGiveWeapon = (uintptr_t)FindPattern((const char*)"\x0F\xB6\x44\x24\x04\x56\x8B\xF1", "xxxxxxxx");
    Log("GiveWeapon", Addr.AGiveWeapon);

    Addr.ASetWeapon = (uintptr_t)FindPattern((const char*)"\x8B\x44\x24\x04\x56\x8B\xF1\x8B\x06\xFF\x90", "xxxxxxxxxxx");
    Log("SetWeapon", Addr.ASetWeapon);

    Addr.AHelpMessage = (uintptr_t)FindPattern((const char*)"\x55\x8B\xEC\x51\x53\x56\x8B\x75\x08\x57", "xxxxxxxxxx");
    Log("HelpMessage", Addr.AHelpMessage);

    Addr.ARequestModel = (uintptr_t)FindPattern((const char*)"\x8B\x44\x24\x04\x8B\x54\x24\x08\x8B\x0C\xC5", "xxxxxxxxxxx");
    Log("RequestModel", Addr.ARequestModel);

    Addr.ALoadAllRequestedModels = (uintptr_t)FindPattern((const char*)"\x80\x3D\x00\x00\x00\x00\x00\x74\x06\xC6\x05", "xx?????xxxx");
    Log("LoadAllRequestedModels", Addr.ALoadAllRequestedModels);

    Addr.AHasModelLoaded = (uintptr_t)FindPattern((const char*)"\x8B\x44\x24\x04\x85\xC0\x78\x10\x8B\x0D", "xxxxxxxxxx");
    Log("HasModelLoaded", Addr.AHasModelLoaded);

    Addr.ASetModelIsNoLongerNeeded = (uintptr_t)FindPattern((const char*)"\x8B\x44\x24\x04\x85\xC0\x78\x0E\x8B\x0D", "xxxxxxxxxx");
    Log("SetModelIsNoLongerNeeded", Addr.ASetModelIsNoLongerNeeded);

    Addr.AGetModelIdFromWeapon = (uintptr_t)FindPattern((const char*)"\x0F\xB6\x44\x24\x04\x69\xC0\x70\x00\x00\x00\x05", "xxxxxxxxxxxx");
    Log("GetModelIdFromWeapon", Addr.AGetModelIdFromWeapon);


    //unsigned char* pWeaponPattern = FindPattern("\x81\xC6\xB8\xAA\xC8\x00\x8B\xCE\xE8", "xx????xxx");
    //if (pWeaponPattern) {
    //    Addr.WeaponInfoTable = *(uintptr_t*)(pWeaponPattern + 2);
    //    Log("WeaponInfoTable", Addr.WeaponInfoTable);
    //} else {
    //    Log("WeaponInfoTable NOT FOUND!");
    //}

    Log("--- Initialization Finished ---");
}

DWORD WINAPI MenuThread(LPVOID lpParam) {
    while (*(uintptr_t*)0xB7CD98 == 0) Sleep(500);
    Sleep(100);
    InitializePlugin();
    
    while (true) {
        // Otwieranie / Zamykanie menu
        // 1. Logika otwierania/zamykania menu (zmiana sterowania)
        if (GetAsyncKeyState(VK_INSERT) & 1) {
            bMenuOpen = !bMenuOpen;
            uintptr_t player = *(uintptr_t*)0xB7CD98;
            if (player) {
                if (bMenuOpen) {
                    freze_player = true;
                    PrintString("~G~MENU OPEN", 1000, 1, false);
                } else {
                    PrintHelpMessage(NULL, 0, 0, 0);
                    freze_player = false;
                    PrintString("~R~MENU CLOSED", 1000, 1, false);
                }
            }
        }

        if (bMenuOpen) {
        	bool left = GetAsyncKeyState(VK_LEFT) & 0x8000;
            bool right = GetAsyncKeyState(VK_RIGHT) & 0x8000;

            DrawMenu();
            if (GetAsyncKeyState('K') & 0x8000) {
                 // Ta funkcja natychmiast zamknie grę do pulpitu
                 exit(0); 
            }
            // Nawigacja GÓRA / DÓŁ
            if (GetAsyncKeyState(VK_UP) & 1) {
                menuIndex--;
                if (menuIndex < 0) menuIndex = 5;
            }
            if (GetAsyncKeyState(VK_DOWN) & 1) {
                menuIndex++;
                if (menuIndex > 5) menuIndex = 0;
            }

            // Obsługa wyboru (ENTER)
            if (GetAsyncKeyState(VK_RETURN) & 1) {
                switch (menuIndex) {
                    case 0: bGodMode = !bGodMode; break;
                    case 1: bInfAmmo = !bInfAmmo; break;
                    case 2: bNever_wanted = !bNever_wanted; break;
                    case 3: bSpeedHack = !bSpeedHack; break;
                    case 4: { spawn_vehicle(); break; } 
                    case 5: {
                    	if(selectedSetIdx >= 0 && selectedSetIdx <= 46){
                            GivePlayerWeapon(selectedSetIdx,500);
                        }
                        break; 
					}
                }
                Beep(1000, 50);
            }

            // Zmiana ID pojazdu w menu (LEWO / PRAWO)
            if (menuIndex == 4) { scroller(left, right, &selectedVehicleID, 400, 611); }
            else if (menuIndex == 5) { scroller(left, right, &selectedSetIdx, 0, 46); }
        }
        //loop
        // --- Logika cheatów w tle ---
        uintptr_t player = *(uintptr_t*)0xB7CD98; 
        if (player) {
            if (bGodMode) {
                *(float*)(player + 0x540) = 999.0f; 
                *(uint8_t*)(player + 0x440) = 31; 
            }
            *(uint8_t*)0x969178 = bInfAmmo ? 1 : 0;
            *(uint8_t*)0x969171 = bNever_wanted ? 1 : 0; 
            *(float*)0xB7CB64 = bSpeedHack ? 0.1f : 1.0f; 
            *(float*)0xB793D8 = 1000.0f; // Stamina
            
            *(float*)0xB7CB64 = freze_player ? 0.00001f : 1.0f; 
        }

        Sleep(10);   
    }
    return 0;
}

extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    if (fdwReason == DLL_PROCESS_ATTACH) {
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MenuThread, NULL, 0, NULL);
        //unsigned long* pGameLoop = (unsigned long*)0x748DA3;
    }
    return TRUE;
}



// Szukamy instrukcji: mov eax, [esp+4]; push esi; mov esi, ecx; mov eax, [esi]; call [eax+0x24]
// To jest unikalna operacja zmiany broni w silniku RenderWare/GTA
static const unsigned char pat_setw_alt[] = { 0x8B, 0x44, 0x24, 0x04, 0x56, 0x8B, 0xF1, 0x8B, 0x06 };
addr = (uintptr_t)FindPattern(pat_setw_alt, "xxxxxxxxx");

if (addr) {
    SetCurrentWeapon = (tSetCurrentWeapon)addr;
    Log("Sukces! Znaleziono SetCurrentWeapon pod: ", addr);
} else {
    Log("NADAL NIE ZNALEZIONO. Probuje metody zapasowej...");
    // Metoda zapasowa: szukanie po instrukcji przypisania slotu broni
    static const unsigned char pat_setw_fallback[] = { 0x8B, 0x44, 0x24, 0x04, 0x56, 0x8B, 0xF1, 0x8B, 0x4E, 0x08 };
    addr = (uintptr_t)FindPattern(pat_setw_fallback, "xxxxxxxxxx");
    SetCurrentWeapon = (tSetCurrentWeapon)addr;
    Log("SetCurrentWeapon (Fallback): ", addr);
}

// Szukamy instrukcji: movzx eax, [esp+4]; push esi; mov esi, ecx; mov eax, [esi+0x5D0]
// To jest serce funkcji dodawania broni w silniku SA
static const unsigned char pat_give_hardcore[] = { 0x0F, 0xB6, 0x44, 0x24, 0x04, 0x56, 0x8B, 0xF1, 0x8B, 0x86 };
addr = (uintptr_t)FindPattern(pat_give_hardcore, "xxxxxxxxxx");

if (addr) {
    GiveWeapon = (tGiveWeapon)addr;
    Log("Sukces! GiveWeapon znaleziony pod: ", addr);
} else {
    Log("GiveWeapon nadal nieuchwytny. Proba szukania po instrukcji slotu...");
    static const unsigned char pat_give_v2[] = { 0x0F, 0xB6, 0x44, 0x24, 0x04, 0x56, 0x8B, 0xF1, 0x69, 0xC0 };
    addr = (uintptr_t)FindPattern(pat_give_v2, "xxxxxxxxxx");
    GiveWeapon = (tGiveWeapon)addr;
    Log("GiveWeapon (Final Fallback): ", addr);
}

// Szukamy tylko: 69 C0 70 00 00 00 05 (to unikalny mnożnik slotów broni w SA)
static const unsigned char pat_super_short[] = { 0x69, 0xC0, 0x70, 0x00, 0x00, 0x00, 0x05 };
addr = (uintptr_t)FindPattern(pat_super_short, "xxxxxxx");

if (addr) {
    // Musimy cofnąć się o ok. 20 bajtów do początku funkcji
    GiveWeapon = (tGiveWeapon)(addr - 0x14); 
    Log("GiveWeapon (SuperShort) znaleziony w okolicy: ", (uintptr_t)GiveWeapon);
}

// Szukamy instrukcji: IMUL EAX, EAX, 70h
// Bajty: 69 C0 70 00 00 00
static const unsigned char pat_give_math[] = { 0x69, 0xC0, 0x70, 0x00, 0x00, 0x00 };
addr = (uintptr_t)FindPattern(pat_give_math, "xxxxxx");

if (addr) {
    // Cofamy się do początku funkcji (zazwyczaj o 20-40 bajtów)
    // Szukamy bajtu 0x56 (PUSH ESI) lub 0x55 (PUSH EBP) przed tym adresem
    uintptr_t start = addr;
    for (int i = 0; i < 64; i++) {
        unsigned char b = *(unsigned char*)(addr - i);
        if (b == 0x56 && *(unsigned char*)(addr - i + 1) == 0x8B && *(unsigned char*)(addr - i + 2) == 0xF1) {
            start = addr - i;
            break;
        }
    }
    GiveWeapon = (tGiveWeapon)start;
    Log("ZNALAZLEM GiveWeapon przez matematyke: ", start);
}

// Funkcja tworząca peda (konstruktor)
typedef void* (__fastcall* tCreatePed)(void* pMemory, int dummy_edx, int pedType, int modelID);
tCreatePed CreatePed = (tCreatePed)0x5E21B0;

// Funkcja rezerwująca pamięć (skrócona wersja operatora new)
typedef void* (__cdecl* tMalloc)(unsigned int size);
tMalloc GtaMalloc = (tMalloc)0x82119A; // Adres operatora new w 1.0 US

// Funkcja dodająca peda do świata gry
typedef void (__cdecl* tAddPedToWorld)(void* pPed);
tAddPedToWorld AddPedToWorld = (tAddPedToWorld)0x563400;


void SpawnBodyguard(int modelID) {
    // 1. Załaduj model
    if (!HasModelLoaded(modelID)) {
        RequestModel(modelID, 6);
        LoadAllRequestedModels(false);
        return; // Spróbuj ponownie w następnej klatce
    }

    // 2. Pobierz pozycję CJ-a
    uintptr_t* pPlayer = (uintptr_t*)0xB7CD98;
    if (!pPlayer || !*pPlayer) return;

    float* pos = (float*)(*pPlayer + 0x14); // Matrix/Pos gracza
    float spawnPos[3] = { pos[12] + 2.0f, pos[13] + 2.0f, pos[14] }; // Spawnuje 2m obok

    // 3. Rezerwacja pamięci na strukturę CPed (rozmiar 0x7C4 dla v1.0)
    void* pNewPed = GtaMalloc(0x7C4);
    if (pNewPed) {
        // 4. Wywołanie konstruktora (pedType 4 = CIVMALE, 5 = CIVFEMALE, 15 = GANG)
        CreatePed(pNewPed, 0, 4, modelID);

        // 5. Ustawienie pozycji nowej postaci
        float* pedPos = (float*)((uintptr_t)pNewPed + 0x14);
        pedPos[12] = spawnPos[0];
        pedPos[13] = spawnPos[1];
        pedPos[14] = spawnPos[2];

        // 6. Dodanie do świata
        AddPedToWorld(pNewPed);

        // 7. Opcjonalnie: Dodaj do grupy gracza (aby go bronił)
        // Adres funkcji CPlayerPed::SetModelIndex to 0x608440 - opcjonalnie
        
        Log("Ochroniarz zespawnowany!");
    }
}


void SpawnArmedBodyguard(int pedModelID, int weaponID, int ammo) {
    // 1. Załaduj modele (Peda i Broni)
    int weaponModel = MyGetWeaponModel(weaponID); // Twoja funkcja GetModelIdFromWeapon
    
    if (!HasModelLoaded(pedModelID) || !HasModelLoaded(weaponModel)) {
        RequestModel(pedModelID, 6);
        RequestModel(weaponModel, 6);
        LoadAllRequestedModels(false);
        return; 
    }

    // 2. Pobierz pozycję gracza (0xB7CD98)
    uintptr_t* pPlayer = (uintptr_t*)0xB7CD98;
    if (!pPlayer || !*pPlayer) return;

    // 3. Stwórz aktora (używając GtaMalloc i CreatePed)
    void* pNewPed = GtaMalloc(0x7C4); // Rozmiar struktury CPed
    if (pNewPed) {
        // Konstruktor (0x5E21B0) - pedType 5 (GANG_STREET)
        CreatePed(pNewPed, 0, 5, pedModelID);

        // Ustaw pozycję 2 metry obok gracza
        float* playerPos = (float*)(*pPlayer + 0x30); // Offset pozycji w Matrixie
        float* newPedPos = (float*)((uintptr_t)pNewPed + 0x30);
        newPedPos[0] = playerPos[0] + 2.0f;
        newPedPos[1] = playerPos[1] + 2.0f;
        newPedPos[2] = playerPos[2];

        // 4. Dodaj do świata (0x563400)
        AddPedToWorld(pNewPed);

        // 5. NADAJ BROŃ (używając Twojego adresu 0x5E6080)
        if (GiveWeapon) {
            // Używamy konwencji __fastcall z dummy_edx
            GiveWeapon((uintptr_t)pNewPed, 0, weaponID, ammo, true);
            
            // Ustaw jako aktywną (0x5E6280 - ten co Ci zadziałał!)
            if (SetCurrentWeaponByID) {
                SetCurrentWeaponByID((uintptr_t)pNewPed, 0, weaponID);
            }
        }

        // 6. OPCJONALNIE: Dodaj do grupy gracza (Bodyguard Mode)
        // Pobierz grupę gracza z offsetu 0x5A0
        uintptr_t playerGroup = *(uintptr_t*)(*pPlayer + 0x5A0);
        if (playerGroup) {
            // Funkcja CPlayerGroup::AddMember (0x5FB010)
            typedef void (__fastcall* tAddMember)(uintptr_t group, int edx, void* pPed);
            tAddMember AddMember = (tAddMember)0x5FB010;
            AddMember(playerGroup, 0, pNewPed);
        }

        Log("Ochroniarz zespawnowany z bronią!");
    }
}

typedef void (__fastcall* tRemoveAllFollowers)(uintptr_t pGroup, int edx);
tRemoveAllFollowers RemoveAllFollowers = (tRemoveAllFollowers)0x5FB160;

void DismissAllBodyguards() {
    uintptr_t* pPlayer = (uintptr_t*)0xB7CD98;
    if (pPlayer && *pPlayer) {
        // Pobierz wskaźnik do grupy gracza (offset 0x5A0 w strukturze CPed)
        uintptr_t pGroup = *(uintptr_t*)(*pPlayer + 0x5A0);
        
        if (pGroup && RemoveAllFollowers) {
            // Wywołanie: (this, dummy_edx)
            RemoveAllFollowers(pGroup, 0);
            Log("Wszyscy ochroniarze zostali odprawieni!");
        }
    }
}

void DrawHUD() {
    UpdateFPS();

    char buffer[64];
    // Kody kolorów: ~g~ zielony, ~w~ biały
    sprintf(buffer, "FPS: ~g~%.1f", fps);

    // Używamy Twojego adresu 0x588BE0 (v1.0 US)
    if (PrintHelpMessage) {
        // bQuick = true, bPermanent = false (bo odświeżamy co klatkę)
        PrintHelpMessage(buffer, true, false, false);
    }
}
float fps = 0.0f;
unsigned int frameCount = 0;
unsigned int lastTime = 0;

void UpdateFPS() {
    unsigned int currentTime = GetTickCount(); // Funkcja z windows.h
    frameCount++;

    // Aktualizuj co 500ms (pół sekundy), żeby licznik nie skakał zbyt szybko
    if (currentTime - lastTime >= 500) {
        fps = frameCount * 1000.0f / (currentTime - lastTime);
        lastTime = currentTime;
        frameCount = 0;
    }
}
